package de.szut.sudoku.game.field;

/**
 * Class for the sudoku-field
 * 
 * @author Marc Huisinga
 * 
 */
public class SudokuField {
	private FieldGroup[] horizontalGroups;
	private FieldGroup[] verticalGroups;
	private FieldGroup[] areaGroups;

	public FieldGroup[] getAreaGroups() {
		return areaGroups;
	}

	public void setAreaGroups(FieldGroup[] areaGroups) {
		this.areaGroups = areaGroups;
	}

	public FieldGroup[] getVerticalGroups() {
		return verticalGroups;
	}

	public void setVerticalGroups(FieldGroup[] verticalGroups) {
		this.verticalGroups = verticalGroups;
	}

	public FieldGroup[] getHorizontalGroups() {
		return horizontalGroups;
	}

	public void setHorizontalGroups(FieldGroup[] horizontalGroups) {
		this.horizontalGroups = horizontalGroups;
	}

	/**
	 * XORs two fields to a field that only contains the values that are
	 * different between both fields
	 * 
	 * @param field1
	 * @param field2
	 * @return
	 */
	public static SudokuField getXORdField(SudokuField field1,
			SudokuField field2) {
		SudokuField resultingField = new SudokuField();

		// iterates through the fields and only adds boxes that contain
		// different states to the resulting field
		int xIndex = 0;
		for (FieldGroup currentGroup : field1.getHorizontalGroups()) {
			int yIndex = 0;
			for (Box currentBox : currentGroup.getBoxes()) {
				if (currentBox.getState() != field2.getHorizontalGroups()[xIndex]
						.getBoxes().get(yIndex).getState()) {
					resultingField.getHorizontalGroups()[xIndex].getBoxes()
							.get(yIndex).setState(currentBox.getState());
				}
				yIndex++;
			}
			xIndex++;
		}

		return resultingField;
	}

	/**
	 * Gets the index of a box in several fieldgroups
	 * 
	 * @param groups
	 * @param box
	 * @return
	 */
	private int getIndexOfBoxInFieldGroupArray(FieldGroup[] groups, Box box) {
		int index = 0;
		int position = -1;
		for (FieldGroup currentGroup : groups) {
			if (currentGroup.containsBox(box)) {
				position = index;
			}
			index++;
		}
		return position;
	}

	/**
	 * Gets the field groups related to the box
	 * 
	 * @param box
	 * @return
	 */
	public FieldGroup[] getRelatedFieldgroups(Box box) {
		// gets the position of a box in all related groups
		int[] boxIndex = getIndexOfBox(box);
		FieldGroup[] relatedFieldGroups = { horizontalGroups[boxIndex[0]],
				verticalGroups[boxIndex[1]], areaGroups[boxIndex[2]] };
		return relatedFieldGroups;
	}

	/**
	 * Gets the index of a box in all fieldgroups
	 * 
	 * @param box
	 * @return
	 */
	public int[] getIndexOfBox(Box box) {
		int[] position = new int[3];

		position[0] = getIndexOfBoxInFieldGroupArray(horizontalGroups, box);
		position[1] = getIndexOfBoxInFieldGroupArray(verticalGroups, box);
		position[2] = getIndexOfBoxInFieldGroupArray(areaGroups, box);

		return position;
	}

	/**
	 * Generates a new SudokuField on instantiation
	 */
	public SudokuField() {
		horizontalGroups = new FieldGroup[9];
		verticalGroups = new FieldGroup[9];
		areaGroups = new FieldGroup[9];
		// Adds FieldGroups to the array
		for (int index = 0; index != 9; index++) {
			horizontalGroups[index] = new FieldGroup();
			verticalGroups[index] = new FieldGroup();
			areaGroups[index] = new FieldGroup();
		}
		int horizontalIndex = 0;
		int areaGroupIndex = 0;
		// Generates boxes and adds them to their respective FieldGroups on a
		// horizontal basis
		for (FieldGroup currentHorizontalGroup : horizontalGroups) {
			for (int verticalIndex = 0; verticalIndex < 9; verticalIndex++) {
				Box currentBox = new Box();
				// Adds the box to the respective horizontal, vertical and area
				// FieldGroup
				currentHorizontalGroup.addBox(currentBox);
				verticalGroups[verticalIndex].addBox(currentBox);
				areaGroups[areaGroupIndex].addBox(currentBox);
				// Jumps to the next AreaGroup if the index enters a new
				// AreaGroup
				if (((verticalIndex + 1) % 3 == 0 || (verticalIndex + 1) % 6 == 0)
						&& (verticalIndex + 1) != 9) {
					areaGroupIndex++;
				}
			}
			horizontalIndex++;
			// Resets the vertical AreaGroup should the index not have entered
			// the new Group yet or adds one to the index should it enter a new
			// one
			if ((horizontalIndex % 3 == 0 || horizontalIndex % 6 == 0)
					&& (horizontalIndex + 1 != 9)) {
				areaGroupIndex++;
			} else {
				areaGroupIndex -= 2;
			}
		}
	}

	/**
	 * Prints the content of the field
	 */
	public void printFieldContent() {
		for (FieldGroup currentGroup : horizontalGroups) {
			for (Box currentBox : currentGroup.getBoxes()) {
				System.out.print(currentBox.getState());
			}
			System.out.println();
		}
	}

	/**
	 * Clones the entire field
	 */
	public SudokuField clone() {
		SudokuField clone = new SudokuField();
		FieldGroup[] clonedHorizontalGroups = new FieldGroup[horizontalGroups.length];
		int index = 0;
		for (FieldGroup currentGroup : horizontalGroups) {
			clonedHorizontalGroups[index] = currentGroup.clone();
			index++;
		}
		clone.setHorizontalGroups(clonedHorizontalGroups);

		FieldGroup[] clonedVerticalGroups = new FieldGroup[verticalGroups.length];
		index = 0;
		for (FieldGroup currentGroup : verticalGroups) {
			clonedVerticalGroups[index] = currentGroup.clone();
			index++;
		}
		clone.setVerticalGroups(clonedVerticalGroups);

		FieldGroup[] clonedAreaGroups = new FieldGroup[areaGroups.length];
		index = 0;
		for (FieldGroup currentGroup : areaGroups) {
			clonedAreaGroups[index] = currentGroup.clone();
			index++;
		}
		clone.setAreaGroups(clonedAreaGroups);

		return clone;
	}

	/**
	 * Checks if this field equals another field
	 * 
	 * @param comp
	 * @return
	 */
	public boolean deepEquals(SudokuField comp) {
		int index = 0;
		for (FieldGroup currentGroup : horizontalGroups) {
			if (!currentGroup.deepEquals(comp.getHorizontalGroups()[index])) {
				return false;
			}
			index++;
		}

		return true;
	}
}
